﻿using System.Collections.Generic;
using System.Linq;
using CardPlay.Model.CardModel;
using CardPlay.Model.GameModel;
using CardPlay.Model.GameModel.Actions;
using CardPlay.Model.GameModel.Presentation;
using CardPlay.Model.Layout;
using CardPlay.Model.Primitives;

namespace CardPlay.DemoPack.Games
{
    [Game("B563E0CC-8878-4B99-B54D-778AED59E8CA", LevelOfDifficulty.Easy, LevelOfSkill.MostlyChance, DurationOfPlay.LessThan5Minutes)]
    public class CaptiveQueens : StandardCardGame
    {
        public CaptiveQueens()
        {
            NamesOfAdditionalPiles.AddRange(new[] {Reserve, Stock, Waste});

            MaximumDeals = 3;
            CardsPerDeal = 1;

            TotalTableaus = 0;
            TotalFoundations = 8;

            InitialDealCompleted += delegate
            {
                foreach (var card in DealScript.SuitSequence.Select(suit => new Card(Rank.Queen, suit) { IsFaceUp = true }))
                    this[Reserve].AddCardToTop(card);
            };
        }

        protected override bool IsComplete
        {
            get { return this[Reserve].IsEmpty; }
        }

        public override ILayoutSite CreatePresentation(IPresentationContext presentation)
        {
            var root = presentation.CreateRowLayout();
            root.Add(presentation.CreateStockWasteLayout(this, SpreadDirection.Vertical));

            var centerLayout = new UniformGridLayout { Columns = 3, Rows = 3 };
            root.Add(centerLayout);

            var style = StandardStyles.Foundation;
            style.MinimumSize = new Size(presentation.Metrics.CardSize.Width * 3 / 2, presentation.Metrics.CardSize.Height * 5 / 4);
            style.UpdateBaseCues = UpdateFoundationCues;

            // Top row
            style.HorizontalAlignment = HorizontalAlignment.Right;
            style.VerticalAlignment = VerticalAlignment.Center;
            centerLayout.Add(presentation.CreatePileVisual(Foundation1, style));

            style.HorizontalAlignment = HorizontalAlignment.Center;
            style.VerticalAlignment = VerticalAlignment.Top;
            centerLayout.Add(presentation.CreatePileVisual(Foundation5, style));

            style.HorizontalAlignment = HorizontalAlignment.Left;
            style.VerticalAlignment = VerticalAlignment.Center;
            centerLayout.Add(presentation.CreatePileVisual(Foundation2, style));

            // Middle row
            style.HorizontalAlignment = HorizontalAlignment.Left;
            style.VerticalAlignment = VerticalAlignment.Center;
            centerLayout.Add(presentation.CreatePileVisual(Foundation6, style));

            var reserveStyle = StandardStyles.Reserve;
            reserveStyle.VerticalAlignment = VerticalAlignment.Center;
            centerLayout.Add(presentation.CreatePileVisual(Reserve, reserveStyle));

            style.HorizontalAlignment = HorizontalAlignment.Right;
            style.VerticalAlignment = VerticalAlignment.Center;
            centerLayout.Add(presentation.CreatePileVisual(Foundation7, style));

            // Bottom row
            style.HorizontalAlignment = HorizontalAlignment.Right;
            style.VerticalAlignment = VerticalAlignment.Center;
            centerLayout.Add(presentation.CreatePileVisual(Foundation3, style));

            style.HorizontalAlignment = HorizontalAlignment.Center;
            style.VerticalAlignment = VerticalAlignment.Bottom;
            centerLayout.Add(presentation.CreatePileVisual(Foundation8, style));

            style.HorizontalAlignment = HorizontalAlignment.Left;
            style.VerticalAlignment = VerticalAlignment.Center;
            centerLayout.Add(presentation.CreatePileVisual(Foundation4, style));

            return root;
        }

        private void UpdateFoundationCues(CardPile pile, BaseCues cues)
        {
            var rank = FoundationRankAt(pile.Location);
            cues.SetRankCue(rank).SetRepeatedRankFoundationSuitCue(PilesNamed(AnyFoundation), rank, 1);
        }

        protected override IEnumerable<Card> CardsInInitialDeal
        {
            get { return DeckFactory.CreateStrippedDeck(face => face.Rank != Rank.Queen); }
        }

        protected override void PrepareDealScript(DealScript script)
        {
            script.DealRemainingFaceDownTo(Stock);
        }

        protected override void OverridePlayingRules(PlayingRules playingRules)
        {
            playingRules.At(Reserve).Permit<RemovePileAction>().Never();
        }

        protected override bool AllowedToBuildFoundation(MovePileAction action)
        {
            var rank = FoundationRankAt(action.Location);

            return rank == Rank.Five
                ? action.IsBuildingFoundation(CardFaceSequence.SameSuitDescendingRank, rank)
                : action.IsBuildingFoundation(CardFaceSequence.SameSuitAscendingRank, rank);
        }

        protected override void OnTransactionEnding()
        {
            var reserve = this[Reserve];
            var actions = reserve
                .Where(card => this.NonEmptyPilesNamed(AnyFoundation).Where(pile => pile.BottomCard.Suit == card.Suit).SelectMany(pile => pile).Count() == CardFace.RanksPerSuit - 1)
                .Select(card => new RemoveCardAction(this, reserve.LocationOf(card)))
                .ToList();

            actions.ForEach(Execute);
        }

        private static Rank FoundationRankAt(PartLocation location)
        {
            return location.GetOrdinal() > 4 ? Rank.Five : Rank.Six;
        }
    }
}